package ldh.im.fxgui.controller.sub;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import ldh.im.dto.enums.MessageType;
import ldh.im.fxbase.dao.ImMessageDao;
import ldh.im.fxbase.data.pojo.ImMessage;
import ldh.im.fxbase.data.pojo.ImSession;
import ldh.im.fxbase.fxanimations.BounceInRightTransition;
import ldh.im.fxbase.fxanimations.BounceOutRightTransition;
import ldh.im.fxbase.util.BeanFactory;
import ldh.im.fxbase.util.FxThreadUtil;
import ldh.im.fxgui.component.cell.SearchMessageView;
import ldh.im.fxbase.util.ThreadPoolUtil;
import ldh.im.fxgui.handle.IMessageShowHandler;
import ldh.im.fxgui.pojo.SearchMessage;
import ldh.im.util.ConfigUtil;
import org.fxmisc.flowless.Cell;
import org.fxmisc.flowless.VirtualFlow;
import org.fxmisc.flowless.VirtualizedScrollPane;

import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;
import java.util.stream.Collectors;

/**
 * 消息-》会话-》消息栏-》历史消息
 */
public class HistoryMessageController implements Initializable, IMessageShowHandler {

    @FXML private Pane root;
    @FXML private Pane messagePane;
    @FXML private Pane showMessagePane;
    @FXML private Pagination pagination;
    @FXML private TextField searchTextField;

    // 查询
    private ObservableList<SearchMessage> items = FXCollections.observableArrayList();
    private VirtualFlow<SearchMessage, ?> messageList;
    private VirtualizedScrollPane virtualizedScrollPane;

    // 查看指定消息
    private ObservableList<SearchMessage> showItems = FXCollections.observableArrayList();
    private VirtualFlow<SearchMessage, ?> showMessageList;

    private ImMessageDao imMessageDao = BeanFactory.getInstance().getBean(ImMessageDao.class);
    private int pageSize = ConfigUtil.getInt("message.historySize", 500);
    private int currentPage = 0;
    private MessageType messageType = null;

    private ImSession currentSession;

    public void setImSession(ImSession imSession) {
        this.currentSession = imSession;
        loadData();
    }

    public void showMessage(ImMessage message) {
        loadShowMessageData(message);
        BounceInRightTransition bounceInRightTransition = new BounceInRightTransition(showMessagePane);
        bounceInRightTransition.playFromStart();
    }

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        messageList = VirtualFlow.createVertical(items, m->{
            SearchMessageView searchMessageView = new SearchMessageView(HistoryMessageController.this, m);
            return Cell.wrapNode(searchMessageView);
        });
        virtualizedScrollPane = new VirtualizedScrollPane<>(messageList);
        virtualizedScrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
        messagePane.getChildren().add(virtualizedScrollPane);

        initShowMessagePane();

        pagination.currentPageIndexProperty().addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                updateData(newValue);
            }
        });

        searchTextField.setOnKeyReleased(e->{
            if (e.getCode() == KeyCode.ENTER || searchTextField.getText().trim().equals("")) {
                loadData();
            }
        });

        root.widthProperty().addListener(l->{
            showMessagePane.setTranslateX(root.getWidth());
        });
    }

    private void initShowMessagePane() {
        showMessageList = VirtualFlow.createVertical(showItems, m->{
            SearchMessageView searchMessageView = new SearchMessageView(HistoryMessageController.this, m);
            return Cell.wrapNode(searchMessageView);
        });
        VirtualizedScrollPane virtualizedScrollPane = new VirtualizedScrollPane<>(showMessageList);
        virtualizedScrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
        VBox.setVgrow(virtualizedScrollPane, Priority.ALWAYS);
        showMessagePane.getChildren().add(virtualizedScrollPane);
        showMessagePane.setTranslateX(root.getWidth());
    }

    private void updateData(Number newValue) {
        int pageIndex = newValue.intValue();
        currentPage = pageIndex;
        loadData();
    }

    private void loadData() {
        boolean isSearchable = false;
        String searchText = searchTextField.getText().trim();
        if (!searchText.trim().equals("")) {
            isSearchable = true;
        }

        boolean searchable = isSearchable;
        ThreadPoolUtil.submit(()->{
            try {
                List<MessageType> messageTypes = messageType == null ? null : Arrays.asList(messageType);
                List<ImMessage> messages =  imMessageDao.getMessagesBySessionId(currentSession.getId(), searchText, messageTypes, currentPage, pageSize);
                long total = imMessageDao.getMessageTotal(currentSession.getId(), searchText, messageTypes);
                int pageNum = (int)(total/pageSize);
                if (pageNum * pageSize < total) pageNum++;
                if (pageNum == 0) {
                    pageNum = 1;
                }
                if (currentPage > pageNum) currentPage = pageNum;
                int pageTotal = pageNum;

                List<SearchMessage> wrapImMessages = messages.stream().map(m->new SearchMessage(m, searchable)).collect(Collectors.toList());
                FxThreadUtil.runLater(()-> {
                    items.clear();
                    items.addAll(wrapImMessages);
                    pagination.setPageCount(pageTotal);
                    messageList.show(0);
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    private void loadShowMessageData(ImMessage message) {
        ThreadPoolUtil.submit(()->{
            try {
                List<MessageType> messageTypes = messageType == null ? null : Arrays.asList(messageType);
                List<ImMessage> preMessages = imMessageDao.getMessagesBySessionId(currentSession.getId(), message.getServerReceiveTime(), messageTypes, 50, true);
                List<ImMessage> nextMessages =  imMessageDao.getMessagesBySessionId(currentSession.getId(), message.getServerReceiveTime(), messageTypes, 50, false);

                List<ImMessage> messages = new ArrayList<>(preMessages.size() + nextMessages.size() + 1);
                messages.addAll(preMessages);
                messages.add(message);
                messages.addAll(nextMessages);
                List<SearchMessage> wrapImMessages = messages.stream().map(m->new SearchMessage(m, false)).collect(Collectors.toList());
                FxThreadUtil.runLater(()-> {
                    showItems.clear();
                    showItems.addAll(wrapImMessages);
                    int idx = showItems.indexOf(new SearchMessage(message, false));
                    showMessageList.show(idx);
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    @FXML
    private void searchAll(MouseEvent mouseEvent) {
        this.messageType = null;
        addClass(mouseEvent);
        loadData();
    }

    @FXML
    private void searchFile(MouseEvent mouseEvent) {
        this.messageType = MessageType.File;
        addClass(mouseEvent);
        loadData();
    }

    @FXML
    private void searchImage(MouseEvent mouseEvent) {
        this.messageType = MessageType.Image;
        addClass(mouseEvent);
        loadData();
    }

    @FXML
    private void searchAudio(MouseEvent mouseEvent) {
        this.messageType = MessageType.Audio;
        addClass(mouseEvent);
        loadData();
    }

    @FXML
    private void searchLink(MouseEvent mouseEvent) {
        this.messageType = MessageType.Url;
        addClass(mouseEvent);
        loadData();
    }

    private void addClass(MouseEvent mouseEvent) {
        Node node = (Node) mouseEvent.getSource();
        Pane pane = (Pane) node.getParent();
        pane.getChildren().forEach(n->n.getStyleClass().remove("current-tab"));
        if (!node.getStyleClass().contains("current-tab")) {
            node.getStyleClass().add("current-tab");
        }
    }

    @FXML
    private void returnBtn(MouseEvent mouseEvent) {
        BounceOutRightTransition bounceOutRightTransition = new BounceOutRightTransition(showMessagePane);
        bounceOutRightTransition.setOnFinished(e->{
            showMessagePane.setTranslateX(root.getWidth());
        });
        bounceOutRightTransition.playFromStart();
    }
}
